/*
 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <sys/prctl.h>
#include <iostream>
#include "source.h"
#include "player.h"
#include "format.h"

#define RETURN_IF_PLAYER_NULL(player) \
do { \
    if ((player) == nullptr) { \
        std::cout<< "must new player firstly"<<std::endl; \
        return; \
    } \
} while(0)

#define COUT_FUNCTION_RESULT(funcRet, funcName) \
std::cout<< funcName << " returns " << funcRet << std::endl

using namespace OHOS::Media;

static uint64_t callBackId = 0;

class PlayerCallbackImpl : public PlayerCallback {
private:
    uint64_t callbackId_;
public:
    PlayerCallbackImpl()
    {
        callbackId_ = callBackId++;
        std::cout<<"now callbackId: " << callbackId_<<std::endl;
    }

    void OnPlaybackComplete() override
    {
        std::cout<<"callbackId: "<<callbackId_<<" OnPlaybackComplete."<<std::endl;
    }

    void OnError(int32_t errorType, int32_t errorCode) override
    {
        std::cout<<"callbackId: "<<callbackId_<<" OnError: errorType "<<errorType <<", errorCode "<<errorCode<<std::endl;
    }

    void OnInfo(int type, int extra) override
    {
        std::cout<<"callbackId: "<<callbackId_<<" OnInfo: type "<<type <<", extra "<<extra<<std::endl;
    }

    void OnVideoSizeChanged(int width, int height) override
    {
        std::cout<<"callbackId: "<<callbackId_<<" OnVideoSizeChanged: width "<<width <<", height "<<height<<std::endl;
    }

    void OnRewindToComplete() override
    {
        std::cout<<"callbackId: "<<callbackId_<<" OnRewindToComplete"<<std::endl;
    }
};


struct MediaPlayer {
    std::shared_ptr<Player> player_ {nullptr};
    std::string sourcePath_ {};
    bool isSingleLoop_ {false};
    int64_t seekPosition_ {0};

};


static void ShowCmdHelp()
{
    printf("Input CMD: quit, new, setSource, prepare, play, isPlaying, pause, stop, seek [ms], loop [bool], isLooping, "
           "getCurrentTime, getDuration, reset, release, setNewCallback, getPlayerState\n");
}

static void NewMediaPlayer(MediaPlayer& mediaPlayer)
{
    if (mediaPlayer.player_ != nullptr) {
        std::cout<<"release previous player"<<std::endl;
        mediaPlayer.player_->Release();
        mediaPlayer.player_.reset();
    }
    mediaPlayer.player_ = std::make_shared<Player>();
}

static void SetMediaPlayerSource(MediaPlayer& mediaPlayer)
{
    RETURN_IF_PLAYER_NULL(mediaPlayer.player_);
    Source source(mediaPlayer.sourcePath_);
    COUT_FUNCTION_RESULT(mediaPlayer.player_->SetSource(source), "setSource");
}

static void PrepareMediaPlayer(MediaPlayer& mediaPlayer)
{
    RETURN_IF_PLAYER_NULL(mediaPlayer.player_);
    COUT_FUNCTION_RESULT(mediaPlayer.player_->Prepare(), "prepare");
}

static void PlayMediaPlayer(MediaPlayer& mediaPlayer)
{
    RETURN_IF_PLAYER_NULL(mediaPlayer.player_);
    COUT_FUNCTION_RESULT(mediaPlayer.player_->Play(), "play");
}

static void IsPlaying(MediaPlayer& mediaPlayer)
{
    RETURN_IF_PLAYER_NULL(mediaPlayer.player_);
    COUT_FUNCTION_RESULT(mediaPlayer.player_->IsPlaying(), "isPlaying");
}

static void PauseMediaPlayer(MediaPlayer& mediaPlayer)
{
    RETURN_IF_PLAYER_NULL(mediaPlayer.player_);
    COUT_FUNCTION_RESULT(mediaPlayer.player_->Pause(), "pause");
}

static void StopMediaPlayer(MediaPlayer& mediaPlayer)
{
    RETURN_IF_PLAYER_NULL(mediaPlayer.player_);
    COUT_FUNCTION_RESULT(mediaPlayer.player_->Stop(), "stop");
}

static void SeekMediaPlayer(MediaPlayer& mediaPlayer)
{
    RETURN_IF_PLAYER_NULL(mediaPlayer.player_);
    COUT_FUNCTION_RESULT(
            mediaPlayer.player_->Rewind(mediaPlayer.seekPosition_, PlayerSeekMode::PLAYER_SEEK_PREVIOUS_SYNC), "seek");
    int64_t currentTime = 0;
    COUT_FUNCTION_RESULT(mediaPlayer.player_->GetCurrentTime(currentTime), "getCurrentTime");
    std::cout<<"now position "<< currentTime << "ms" <<std::endl;
}

static void LoopPlay(MediaPlayer& mediaPlayer)
{
    RETURN_IF_PLAYER_NULL(mediaPlayer.player_);
    COUT_FUNCTION_RESULT(mediaPlayer.player_->EnableSingleLooping(mediaPlayer.isSingleLoop_), "loop");
}

static void IsLooping(MediaPlayer& mediaPlayer)
{
    RETURN_IF_PLAYER_NULL(mediaPlayer.player_);
    COUT_FUNCTION_RESULT(mediaPlayer.player_->IsSingleLooping(), "isLooping");
}

static void GetCurrentPlayTime(MediaPlayer& mediaPlayer)
{
    RETURN_IF_PLAYER_NULL(mediaPlayer.player_);
    int64_t currentTime = 0;
    COUT_FUNCTION_RESULT(mediaPlayer.player_->GetCurrentTime(currentTime), "getCurrentTime");
    std::cout<< "now playing at "<< currentTime<< "ms"<<std::endl;
}

static void GetDuration(MediaPlayer& mediaPlayer)
{
    RETURN_IF_PLAYER_NULL(mediaPlayer.player_);
    int64_t duration = 0;
    COUT_FUNCTION_RESULT(mediaPlayer.player_->GetDuration(duration), "getDuration");
    std::cout<<"duration "<< duration << "ms" <<std::endl;
}

static void ResetPlayer(MediaPlayer& mediaPlayer)
{
    RETURN_IF_PLAYER_NULL(mediaPlayer.player_);
    COUT_FUNCTION_RESULT(mediaPlayer.player_->Reset(), "reset");
}

static void ReleasePlayer(MediaPlayer& mediaPlayer)
{
    RETURN_IF_PLAYER_NULL(mediaPlayer.player_);
    COUT_FUNCTION_RESULT(mediaPlayer.player_->Release(), "release");
}

static void SetPlayerCallback(MediaPlayer& mediaPlayer)
{
    RETURN_IF_PLAYER_NULL(mediaPlayer.player_);
    auto callback = std::make_shared<PlayerCallbackImpl>();
    mediaPlayer.player_->SetPlayerCallback(callback);
}

static void GetPlayerState(MediaPlayer& mediaPlayer)
{
    RETURN_IF_PLAYER_NULL(mediaPlayer.player_);
    int32_t state = 0;
    COUT_FUNCTION_RESULT(mediaPlayer.player_->GetPlayerState(state), "getPlayerState");
    std::string stateString;
#define CASE_STATE(state) \
case state: \
    stateString = #state; \
    break
    switch (state) {
        CASE_STATE(PLAYER_STATE_ERROR);
        CASE_STATE(PLAYER_IDLE);
        CASE_STATE(PLAYER_INITIALIZED);
        CASE_STATE(PLAYER_PREPARING);
        CASE_STATE(PLAYER_PREPARED);
        CASE_STATE(PLAYER_STARTED);
        CASE_STATE(PLAYER_PAUSED);
        CASE_STATE(PLAYER_STOPPED);
        CASE_STATE(PLAYER_PLAYBACK_COMPLETE);
        default:
            stateString = "PLAYER_STATE_ERROR";
    }
    std::cout<<"player state: "<< stateString<<std::endl;
}



std::map<std::string, std::function<void(MediaPlayer&)>> cmdFuncMaps = {
        {"new", NewMediaPlayer},
        {"setSource", SetMediaPlayerSource},
        {"prepare", PrepareMediaPlayer},
        {"play", PlayMediaPlayer},
        {"isPlaying", IsPlaying},
        {"pause", PauseMediaPlayer},
        {"stop", StopMediaPlayer},
        {"seek", SeekMediaPlayer},
        {"loop", LoopPlay},
        {"isLooping", IsLooping},
        {"getCurrentTime", GetCurrentPlayTime},
        {"getDuration", GetDuration},
        {"reset", ResetPlayer},
        {"release", ReleasePlayer},
        {"setNewCallback", SetPlayerCallback},
        {"getPlayerState", GetPlayerState}
};


static void ProcessCmd()
{
    MediaPlayer mediaPlayer {};
    std::string cmd;
    while (std::cin>> cmd) {
        if(cmd == "quit"){
            break;
        }
        if (cmd == "setSource") {
            std::cin>>mediaPlayer.sourcePath_;
        } else if (cmd == "loop") {
            std::cin>>mediaPlayer.isSingleLoop_;
        } else if (cmd == "seek") {
            std::cin>> mediaPlayer.seekPosition_;
        }
        auto ite = cmdFuncMaps.find(cmd);
        if (ite != cmdFuncMaps.end()) {
            ite->second(mediaPlayer);
        }
        ShowCmdHelp();
    }
    ReleasePlayer(mediaPlayer);
}


int main(int argc, char** argv)
{
    prctl(PR_SET_NAME, "PlayerSample", 0, 0, 0);
    ShowCmdHelp();
    ProcessCmd();
    return 0;
}